package meta

import (
	"database/sql"
	"fmt"
	"game/game-db/orm/column"
	"game/game-db/orm/metag"
	"reflect"
	"strconv"
	"strings"
)

type IEntityBaseMeta interface {
	Columns() []column.IColumnMeta
	NewEntityPtr() reflect.Value
	ColumnMeta(columnName string) column.IColumnMeta
	FillValue(entity reflect.Value, columnTypes []*sql.ColumnType, dest []interface{})
	FieldNameByColumnName(columnName string) string
	ColumnNameByFieldName(fieldName string) string
}

type IEntityMeta interface {
	IEntityBaseMeta
	TableName() string
	TableCharset() metag.Charset
	TableComment() string
	PrimaryKey() *column.PrimaryKeyColumnMeta
	PrimaryKeyName() string
	PrimaryKeyStrategy() metag.KeyStrategy
	GetPrimaryKeyValue(entity reflect.Value) interface{}
	Indexes() []*metag.IndexMetag

	TakeInsertValue(entity reflect.Value) []interface{}
	FillKeyValue(entity reflect.Value, lastInsertId int64)
	TakeUpdateValue(entity reflect.Value) []interface{}
}

func fieldsOf(entityStruct interface{}) []reflect.StructField {
	structFields := make([]reflect.StructField, 0, 20)

	rValue := reflect.ValueOf(entityStruct)
	var rType reflect.Type

	if rValue.Kind() == reflect.Interface || rValue.Kind() == reflect.Ptr {
		rType = rValue.Elem().Type()
		rValue = rValue.Elem()
	} else {
		rType = rValue.Type()
	}
	for i := 0; i < rType.NumField(); i++ {
		field := rType.Field(i)
		if field.Anonymous {
			//匿名嵌套的结构体具有父类的概念
			superValue := rValue.Field(i).Interface()
			structFields = append(structFields, fieldsOf(superValue)...)
		} else {
			structFields = append(structFields, field)
		}
	}
	return structFields
}

// 解释字段上的tag描述
func parseTag(tagDescriptor string) metag.ColumnMetag {
	var columnMetag metag.ColumnMetag

	keyValues := strings.SplitN(tagDescriptor, ";", -1)
	for _, v := range keyValues {
		keyValue := strings.SplitN(v, "=", -1)
		switch keyValue[0] {
		case "name":
			columnMetag.Name = keyValue[1]
		case "comment":
			columnMetag.Comment = keyValue[1]
		case "length":
			intv, _ := strconv.ParseInt(keyValue[1], 10, 32)
			columnMetag.Length = int32(intv)
		case "extra":
			columnMetag.Extra = keyValue[1]
		case "readonly":
			columnMetag.ReadOnly = true
		case "formatter":
			columnMetag.Formatter = keyValue[1]
		default:
		}
	}
	return columnMetag
}

func columnMetaOf(structField reflect.StructField, columnMetag metag.ColumnMetag) column.IColumnMeta {
	if structField.Type.Kind() == reflect.Int8 {
		return column.NewInt8Field(structField, columnMetag)
	} else if structField.Type.Kind() == reflect.Int16 {
		return column.NewInt16Field(structField, columnMetag)
	} else if structField.Type.Kind() == reflect.Int32 {
		return column.NewInt32Field(structField, columnMetag)
	} else if structField.Type.Kind() == reflect.Int64 {
		return column.NewInt64Field(structField, columnMetag)
	} else if structField.Type.Kind() == reflect.Int {
		return column.NewInt64Field(structField, columnMetag)

	} else if structField.Type.Kind() == reflect.Float32 {
		return column.NewFloat32Field(structField, columnMetag)
	} else if structField.Type.Kind() == reflect.Float64 {
		return column.NewFloat64Field(structField, columnMetag)

	} else if structField.Type.Kind() == reflect.Bool {
		return column.NewBoolField(structField, columnMetag)

	} else if structField.Type.Kind() == reflect.String {
		return column.NewStringField(structField, columnMetag)

	} else if structField.Type.Kind() == reflect.Struct {
		if structField.Type.Name() == "Time" {
			return column.NewTimeField(structField, columnMetag)
		} else {
			return column.NewStructField(structField, columnMetag)
		}
	} else if structField.Type.Kind() == reflect.Map {
		return column.NewStructField(structField, columnMetag)
	} else if structField.Type.Kind() == reflect.Slice {
		return column.NewStructField(structField, columnMetag)
	} else if structField.Type.Kind() == reflect.Array {
		return column.NewStructField(structField, columnMetag)

	} else {
		panic(fmt.Sprintf("unsupport field type of %v %v", structField.Type.Name(), structField.Name))
	}
}
